home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 April: Mac OS SDK / Dev.CD Apr 96 SDK / Dev.CD Apr 96 SDK1.toast / Development Kits (Disc 1) / OpenDoc / Documentation / Tech Notes & Articles / Recipes / Data Interchange / Drag and Drop Recipes < prev    next >
Encoding:
Text File  |  1995-11-08  |  24.9 KB  |  534 lines  |  [TEXT/ttxt]

  1. OpenDoc™ Recipes
  2.  
  3.  
  4. Drag-and-Drop Recipes
  5. By The OpenDoc Design Team
  6. November 6, 1995
  7.  
  8. © 1993-1995  Apple Computer, Inc. All Rights Reserved.
  9. Apple, the Apple logo, AppleScript, Bento, Macintosh, QuickTime, and OpenDoc are 
  10. registered trademarks of Apple Computer, Inc.
  11. Finder, Mac, and QuickDraw are trademarks of Apple Computer, Inc. 
  12. SOM, SOMObjects, and System Object Model are licensed trademarks of IBM Corporation. 
  13.  
  14. Changes since DR3
  15.  
  16. 1) Added discussion of an embedded frame's in-limbo flag.
  17. 2) When a file is dropped, the destination needs to check to see whether the file is corresponding to an alias file. 
  18. 3) Fixed ISO String prefixes.
  19. 4) Caller of StartDrag needs to release the returned destination part.
  20. 5) Source part needs to use kODCloneCut (not kODCloneCopy) as the default.
  21.  
  22. Changes since DR2
  23.  
  24. 1) Added kODPropMouseDownOffset.
  25. 2) Fixed refcounting method names.
  26. 3) Fixed parameter passing errors with SetValue and GetValue.
  27.  
  28. About Drag-and-Drop Recipes 
  29.  
  30. The Drag and Drop protocol provides a direct manipulation alternative to the clipboard for copying and moving data between parts in a document, between documents and between documents and the desktop. 
  31.  
  32. This document contains a number of recipes for transferring data through the OpenDoc Drag-and-Drop mechanism.  Refer to the Class Documentation for a description of individual methods.
  33.  
  34. The examples use exception handling in the form of TRY…CATCH_ALL…ENDTRY blocks to catch exceptions, and assumes that errors returned by SOM methods are automatically thrown upon return.
  35.  
  36. WARNING: These recipes are not actual code, and may contain errors. They serve as examples of how to use the OpenDoc APIs to  accomplish specific objectives in particular circumstances. Actual part code usually needs to draw from several recipes to handle the variety of conditions that occur in a real application.
  37.  
  38. The Drag-and-Drop Focus (or the lack of it)
  39.  
  40. There is no Drag-and-Drop focus because only one part can be initiating a drop at a time and the drag is synchronous.
  41.  
  42. Undo of a Drag-and-Drop
  43.  
  44. A copy or move using Drag-and-Drop should be undoable.  An undo transaction should be started by the part initiating the drag; the part receiving the drop may add undo actions to the transaction.  The transaction should be ended after the drop completed by the part initiating the drag.  See the Undo Recipes document in the Edit Menu folder for details.
  45.  
  46. Embedded Frames and their In-Limbo flag
  47.  
  48. When an embedded frame is dragged or dropped, parts must set the in-limbo flag  of the frame correctly.  Parts are also responsible for setting the in-limbo flag  when a drag and drop operation is undone, redone, or commited via their DisposeActionState method.   The code examples here demonstrate the proper manipulation of this flag during the drag and drop, but do not cover how your part must support the in-limbo flag in your UndoAction, RedoAction, or DisposeActionState methods.  The complete recipes for setting this flag, and for the actions to take in DisposeActionState, are discussed in the OpenDoc Programmer's Guide.
  49.  
  50.  
  51. Recipes
  52.  
  53.  
  54. Preparing for a Drop:
  55.  
  56. A part which can receive a drop should call IsDroppable on all its display frames which can accept a drop. This can be done during ODPart::DisplayFrameAdded or ODPart::DisplayFrameConnected.
  57.  
  58. SOM_Scope void  SOMLINK MyPartDisplayFrameAdded(AppleTest_Container *somSelf, Environment *ev,
  59.   ODFrame* frame)
  60. {
  61.  ......
  62.  frame->SetDroppable(ev, kODTrue);
  63.  ......
  64. }
  65.  
  66. If your part does not call SetDroppable on a display frame, the part will not receive any DragEnter, DragWithin, DragLeave and Drop from any facet on that frame.
  67.  
  68. If your part no longer wishes to receive drops from a particular frame, it can call ODFrame::SetDroppable with kODFalse.
  69.  
  70.  
  71. Detecting a Drag (Handling Mouse-Down Events):
  72.  
  73. Note that a drag can be initiated by any part even when the part does not have any display frame that is set to receive drops.
  74.  
  75. A drag is initiated by a mouse-down event — kODEvtMouseDown or kODEvtMouseDownEmbedded — received by your HandleEvent method. (For more information on handling events, see the Basic Event Handling recipe.) When handling a mouse-down event, check to see if the coordinates of the mouse-down in the facet are within an item that can be dragged, such as a content item, a text selection, or a selected or bundled embedded frame.
  76.  
  77. If you’ve determined that the mouse-down is on something draggable, you need to wait and see if the user actually starts dragging, or just releases the mouse without moving it (a click.) On the Macintosh, call the Drag Manager’s WaitMouseMoved routine. This takes no parameters and returns a value of kODTrue if the mouse has moved more than two pixels and a drag is about to begin, or kODFalse if the user released the mouse button without moving the mouse more than two pixels. In the latter case, you should treat the event as just a regular click.
  78.  
  79. The above discussion is actually slightly simplified: this is what applies in the usual case where the facet is in an active window. Handling a mouse-down in an inactive window requires a bit more work. The OpenDoc Human Interface Guidelines state that a click in an inactive window should just activate the window; the user does not “click through” to the content. However, the user can drag an item from an inactive window without activating the window. That last bit is important: if the window activated, it might cover up the very place in another window that you wanted to drag the item to. (Those who used a Mac before System 7 will recall how much fun this was.)
  80.  
  81. On the Mac this breaks down into two cases, one where the entire document (process, layer) is inactive, and one where it’s active but the particular window is inactive. These cases look the same to the user, but the Macintosh’s layer model makes them different to implement.
  82.  
  83. If the document’s process/layer is active, you receive a normal kODEvtMouseDown or kODEvtMouseDownEmbedded event. The logic to handle these looks like this:
  84.  
  85. if the mouse is over a draggable item and WaitMouseMoved() returns true
  86.  
  87.  Initiate a drag (see Initiating a drag)
  88.  
  89. else if the window is active
  90.  
  91.  handle the click normally (perhaps starting a drag-selection)
  92.  
  93. else
  94.  
  95.  activate the window
  96.  
  97. return kODTrue
  98.  
  99. In the case where the document’s process/layer is inactive, you receive a different event, either kODEvtBGMouseDown or kODEvtBGMouseDownEmbedded. These have to be handled somewhat differently:
  100.  
  101. if the mouse is over a draggable item and WaitMouseMoved() returns true
  102.  
  103.  Initiate a drag (see Initiating a drag)
  104.  
  105.  return kODTrue;
  106.  
  107. else
  108.  
  109.  return kODFalse;
  110.  
  111. Note that no attempt is made to handle anything other than a drag (in particular the window is not activated) and that you have to return kODFalse if there was no drag. This is so that OpenDoc can tell the Process Manager whether or not the document process needs to be activated after your handler returns.
  112.  
  113. Warning: It’s important not to do anything but initiate a drag when handling a BGMouseDown event. At the time you’re sent the event, the process is in a Process Manager callback similar to a drag handler, and the same rules apply: interacting with the user by putting up a dialog, or switching processes, will hang or crash the system.
  114.  
  115.  
  116. Initiating a drag:
  117.  
  118. Once a part knows that a drag is to be initiated, the source part should call ODSession's GetDragAndDrop to get the ODDragAndDrop object. Then it should call ODDragAndDrop's Clear and ODDragAndDrop's GetContentStorageUnit to get the ODStorageUnit to where data of the dragged object(s) is copied. Then it can initiate drag by calling ODDragAndDrop's StartDrag. An image for dragging feedback is provided by the source part.
  119.  
  120. Drag Outline:
  121.  
  122. On the Macintosh, the imageType is kODDragImageRegionHandle and the image data is a byte array containing the handle (not the content of the handle) of the drag region in global coordinates. It is the part's responsiblity to build this drag region.
  123.  
  124. Ref con for StartDrag:
  125.  
  126. The ODEventData pointer is passed in as the refCon. This information is important for interoperating with the Macintosh Drag Manager.
  127.  
  128. Dragging a frame:
  129.  
  130. In general, it is a bad idea to drag a frame into itself. Therefore, if you are dragging a frame or a set of frames, you should call ODFrame::SetDragging using kODTrue. This will tell OpenDoc Drag-and-Drop that these frames are not supposed to accept this drop.
  131.  
  132. Mouse down offset:
  133.  
  134. The source part should write out the mouse down offset from the top-left corner of the selectuib. This enables the destination part to place the selection at the correct offset from the mouse up position when it is dropped.
  135.  
  136. Clone Kind:
  137.  
  138. If the source part needs to clone its content (whether it is intrinsic or embedded parts), it should use kODCloneCut. kODCloneCopy is only used when the content cannot be moved. If kODCloneCopy is used, kODDropMove will never be returned as the return result of ODDragAndDrop::StartDrag.
  139.  
  140. Recipe:
  141.  
  142. // Get the ODDragAndDrop object from the session.
  143.  
  144. ODDragAndDrop* dragAndDrop = 
  145.      somSelf->GetStorageUnit(ev)->GetSession(ev)->GetDragAndDrop(ev);
  146.  
  147. // Reinitialize the ODDragAndDrop object.
  148.  
  149. dragAndDrop->Clear(ev);
  150.  
  151. // Get the Storage Unit where data for dragged objects are going to be written.
  152.  
  153. ODStorageUnit* storageUnit = dragAndDrop->GetContentStorageUnit(ev);
  154.  
  155. // Write out the data.
  156. // Please refer to the following sections in Clipboard recipes:
  157. // Annotating the clipboard with a frame shape
  158. // Writing a frame shape to the clipboard
  159. // Always use BeginClone and EndClone
  160. // Putting intrinsic content on the clipboard
  161. // Copying content to the clipboard
  162. // Putting a single embedded frame on the clipboard.
  163. .......
  164.  
  165. // If you are dragging a frame or a set of frames, you should notify OpenDoc Drag-and-Drop
  166. // that this frame is being dragged.
  167.  
  168. frame->SetDragging(ev, kODTrue);
  169.  
  170. // Anticipate a move by marking all dragged frames as in-limbo
  171. frame->SetInLimbo(ev, kODTrue);
  172.  
  173. // Write out the mouse down offset.
  174. storageUnit->AddProperty(ev, kODPropMouseDownOffset);
  175. storageUnit->AddValue(ev, kODPoint);
  176.  
  177. // Calculate offset
  178. ...
  179. // Set up byte array
  180. ...
  181. // Write out the mouse down offset.
  182. storageUnit->SetValue(ev, &ba);
  183.  
  184. // Create byte arrays for dragRn and refCon (which is the event record)
  185.  
  186. ODByteArray dragRgnBA;
  187. dragRgnBA._length = sizeof(RgnHandle);
  188. dragRgnBA._maximum = sizeof(RgnHandle);
  189. dragRgnBA._buffer = &dragRgn;
  190.  
  191. ODByteArray eventBA;
  192. eventBA._length = sizeof(ODEventData*);
  193. eventBA._maximum = sizeof(ODEventData*);
  194. eventBA._buffer = &event;        // event is of type ODEventData*.
  195.  
  196. // Initiate the drag
  197.  
  198. ODPart* destPart;
  199.  
  200. dropResult = 
  201.  dragAndDrop->StartDrag(sourceFrame, kODDragImageRegionHandle, &dragRgnBA, &destPart, &eventBA);
  202.  
  203. // If you have set any frame to not accept any drop, unset it.
  204.  
  205. frame->SetDragging(ev, kODFalse);
  206.  
  207. // If the drag was not a move, dragged frames are no longer in limbo
  208. if ( dropResult != kODDropMove )
  209. {
  210.   frame->SetInLimbo(ev, kODFalse);
  211. }
  212.  
  213. if ( dropResult == kODDropMove )
  214. {
  215.   // If a frame or set of frames was moved, old facets must be removed.
  216.   // The source part must take into account that a moved frame may now be embedded in another part,
  217.   // and may have new facets in its new containing frame.
  218.   // The source part cannot use the moved frame's facet iterator.
  219.   // It must instead use the containing facet's iterator to identify the facets to be deleted.
  220.   ...
  221. }
  222.  
  223. if (destPart != kODNULL)
  224.   destPart->Release(ev);
  225.  
  226. Tracking a drag:
  227.  
  228. Entering a Part’s facet:
  229.  
  230. ODPart's DragEnter is called when the mouse enters a facet. The part should examine the available data types of the dragged items using the ODDragItemIterator passed in. If the part can handle a drop of the dragged object, it should provide the appropriate feedback (e.g., adorning the droppable frame, changing the cursor). If the destination part cannot handle the data types, nothing should be done.
  231.  
  232. If there is more than one drag item, the Part should make sure that it can accept all the drag items. If there is one or more drag item that the Part cannot accept, it should return kODFalse from DragEnter and do no visual feedback.
  233.  
  234. The following code fragment shows a simple example where a part which can only accepts one kind (kMyKind) determines whether the current drag can be accepted.
  235.  
  236. ODBoolean CanAcceptThisDrop(Environment* ev, ODDragItemIterator* dragInfo)
  237. {
  238.  // assuming that we can accept all the drag items
  239.  ODBoolean canAccept = kODTrue;
  240.  for (ODStorageUnit* dragSU = dragInfo->First(ev);
  241.    (dragInfo->IsNotComplete(ev) && (canAccept == kODTrue));
  242.    dragSU = dragInfo->Next(ev))
  243.  {
  244.   if (dragSU->Exists(ev, kODPropContents, kMyKind, 0) == kODFalse)
  245.    canAccept = kODFalse;
  246.  }
  247.  return canAccept;
  248. }
  249.  
  250. However, checking the kinds is not enough to provide the full feedback according to the Macintosh Drag Manager guidelines. The guidelines dictate that no highlighting should be shown on the frame from which the drag is initiated until the drag has left and returned to the frame. Finder (for system 7.5) provides an excellent example. When you drag a file from the Finder, the window in which the file resides is not highlighted. However, when you drag the file out of the window and back to the window, highlighting is shown.
  251.  
  252. In order to help part developers implement this, OpenDoc Drag-and-Drop provides two Drag Attributes:
  253.  
  254. kODDragIsInSourceFrame shows that the drag still hasn't left the source frame. This can be used in place of dragHasLeftSenderWindow of the Drag Manager which can only handle applications.
  255. kODDragIsInSourcePart shows that the drag still hasn't left the source part. There is no Macintosh Drag Manager equivalent of this status code.
  256.  
  257. ODDragResult MyPartDragEnter(MyPart* somSelf,
  258.       Environment* ev,
  259.       ODDragItemIterator* dragInfo,
  260.       ODFacet* facet,
  261.       ODPoint where)
  262. {
  263.  
  264.  // Get the Drag-and-Drop object associated with this drag.
  265.  // Note that the the session from dropSU may not be the same as the session of the
  266.  // target part.
  267.  ODDragAndDrop* dad = dropSU->GetSession(ev)->GetDragAndDrop(ev);
  268.  ODULong dragAttributes = dad->GetDragAttributes(ev);
  269.  
  270.  // Determine whether we can handle this drag.
  271.  ODBoolean canAccept = CanAcceptThisDrag(ev, dragInfo);
  272.  
  273.  // If we can accept the data and the drag has left the source frame, 
  274.  // do some adornment.
  275.  // Here we are going to put up drag hilite on the frame itself.
  276.  
  277.  if ((canAccept == kODTrue) && !(dragAttributes & kODDragIsInSourceFrame)) {
  278.   // Use FocusLib to set up the grafport.
  279.   // Since this is a stack variable, it will be cleaned up when we exit.
  280.   CFocus prepareForRendering(ev, facet);
  281.  
  282.   // Get the platform frame shape.
  283.   ODFrame* displayFrame = facet->GetFrame(ev);
  284.   ODShape* frameShape = displayFrame->AcquireFrameShape(ev, kODNULL);
  285.   RgnHandle bRgn = frameShape->GetQDRegion(ev);
  286.   frameShape->Release(ev);
  287.  
  288.   // Use the Macintosh Drag Manager to show the hilite.
  289.   // Note that currently the drag hilite drawn by Macintosh Drag Manager only shows up
  290.   // on a white background. If you have any other background color, the drag hilite will
  291.   // not be shown.
  292.   // You can also provide your own hiliting without going through the Drag Manager.
  293.  
  294.   // Use the Drag Reference to call the Macintoh Drag Manager.
  295.   ShowDragHilite(dad->GetDragReference(ev), bRgn, true);
  296.  }
  297.  
  298.  // Return ODDragResult to show whether a Drop can happen in this facet.
  299.  return canAccept;
  300. }
  301.  
  302.  
  303. Within a Part’s facet:
  304.  
  305. ODPart's DragWithin is called continuously when the mouse is still in the facet. This allows the part to do any processing desired. One good example is when the frame has several hot spots where objects can be dropped. If the mouse is not over these hot spots, the cursor may need to be changed to reflect that the no dropping can be done there even though it is still in a droppable frame. Again, a ODDragItemIterator is passed in so that the part can examine the availabe data types of the dragged objects.
  306.  
  307. ODPart's DragWithin also provides a chance for the part to examine the state of the machine. For example, some part may want to find out whether the modifier keys are down or not.
  308.  
  309.  
  310. Leaving a Part’s facet:
  311.  
  312. ODPart's DragLeave is called when the mouse leaves a droppable frame. This allows the part to clean up after a drag within it (e.g., removing adornment on the frame, changing the cursor back to its original form).
  313.  
  314.  
  315. Receiving a Drop:
  316.  
  317. If the mouse is released within a facet, ODPart's Drop is called on the part which owns the facet. The part can then figure out whether it can receive the dragged object using the ODDragItemIterator passed in.
  318.  
  319. OpenDoc Human Interface Specification defines guidelines for the destination part to decide whether a drop should be a move or a copy. OpenDoc Drag-and-Drop provides this information to the part through the OpenDoc Drag Attributes (not to be confused with the attributes from the Macintosh Drag Manager). The part can get the OpenDoc Drag Attributes through the ODDragAndDrop object. When ODDragAndDrop::GetDragAttributes is called, a ODULong is returned. The value of the OpenDoc Drag Attributes reflects important information about the Drop:
  320.  
  321. kODDropIsInSourceFrame: The drop happens in the frame where the drag is initiated.
  322. kODDropIsInSourcePart: The drop happens in the part when the drag is initiated.
  323. kODDropIsMove: The drop is a move.
  324. kODDropIsCopy: The drop is a copy.
  325. kODDropIsPasteAs: The destination part should put up the Paste As Dialog and enable the user to take further actions.
  326.  
  327. The destination part should look for kODPropMouseDownOffset to position the dropped data according to the original mousedown offset.
  328.  
  329. ODDropResult MyPartDrop(MyPart* somSelf,
  330.               Environment* ev,
  331.               ODDragItemIterator *dropInfo,
  332.               ODFacet* facet,
  333.               ODPoint where)
  334. {
  335.  ......
  336.  
  337.  // Determine whether we can handle this drag.
  338.  ODBoolean canAccept = CanAcceptThisDrag(ev, dragInfo);
  339.  
  340.  // Get the OpenDoc Drop Attirbutes for this drop
  341.  ODDragAndDrop* dad = somSelf->GetStorageUnit(ev)->GetSession(ev)->GetDragAndDrop(ev);
  342.  ODULong dragAttributes = dad->GetDragAttributes(ev);
  343.  
  344.  ODDropResult dropResult = kODDropFail;
  345.  
  346.  if (dragAttributes & kODDropIsMove) {
  347.   // This is a move
  348.   // Refer to the following Clipboard recipes:
  349.   // Incorporating content from the clipboard
  350.   // Embedding a part from the clipboard
  351.   ......
  352.   dropResult = kODDropMove;
  353.  }
  354.  else if (dragAttributes & kODDropIsCopy) {
  355.   // This is a copy
  356.   // Refer to the following Clipboard recipes:
  357.   // Incorporating content from the clipboard
  358.   // Embedding a part from the clipboard
  359.   ......
  360.   dropResult = kODDropCopy;
  361.  }
  362.  else if (dragAttributes & kODDropIsPasteAs) {
  363.   // Refer to the following Clipboard recipe:
  364.   // Embedding a Part via the Paste As Dialog
  365.   .......
  366.  }
  367.  
  368.  // For each frame embedded during the drop, remember its current in-limbo status
  369.  // before setting the status to false (not in-limbo)
  370.  wasInLimbo = frame->IsInLimbo(ev); // for use during undo
  371.  frame->SetInLimbo(ev, kODFalse);
  372.  
  373.  return dropResult;
  374. }
  375.  
  376. Move, Copy, Paste As:
  377.  
  378. The destination part can also override a move and make the drop into a copy. However, changing a copy to a move is not allowed.
  379.  
  380. Returning the Drop Result:
  381.  
  382. After the destination part has responded to the drop, it needs to notify the source part whether it has accepted the drop as a move or a copy. The following are the predefined values of ODDropResult:
  383.  
  384.  kODDropFail: The drop has failed.
  385.  kODDropCopy: The drop is a copy.
  386.  kODDropMove: The drop is a move.
  387.  kODDropUnfinished: The drop is not finished. This value should never be returned by the destination part. It is only use by OpenDoc when DragAndDrop::StartDrag returns immediately in the case of an asynchronous drag.
  388.  
  389. This result is returned to the source part (via the system) whether the drop is accepted and what action the source part should take.
  390.  
  391.  
  392. Incorporating data from a non-OpenDoc document
  393.  
  394. When a Part's Drop method is called, it is given an ODDragItemIterator. ODDragItemIterator allows the user to access a collection of Drag Items.  As described above, it is the receiver's (or the destination part's) responsibility to iterate through all the Drag Items to find out whether it can receive the Drop. 
  395.  
  396. If the Drop comes from an OpenDoc part, there is only one Drag Item in the collection. If the Drop comes from a non-OpenDoc application (e.g., the Finder), there may be one or more Drag Item. If there is more than one Drag Item, the destination part can only accept the drop if it can accept all the drag items.
  397.  
  398. If the Drop is initiated in the Finder and the dragged object is a file, the Storage Unit of the Drag Item corresponding to the dragged object will contain a special value type in its Contents Property. The value type is "+//ISO 9070/ANSI::113722::US::CI LABS::MacOS:FileType:" followed by the actual file type of the file. For example, if the user drags a 'TEXT' file into a Part, "+//ISO 9070/ANSI::113722::US::CI LABS::MacOS:FileType:TEXT" will appear in the Contents Property of the Storage Unit of the Drag Item.
  399.  
  400. In order to access the file, the Part needs to get the HFSFlavor from the value of type "+//ISO 9070/ANSI::113722::US::CI LABS::MacOS:ScrapType:hfs " in the Contents Property. The struct is defined in Drag.h of the Macintosh Drag Manager as follows:
  401.  
  402. struct HFSFlavor {
  403.  OSType fileType;
  404.  OSType fileCreator;
  405.  unsigned short fdFlags;
  406.  FSSpec fileSpec;
  407. };
  408. typedef strcut HFSFlavor HFSFlavor;
  409.  
  410. Example:
  411.  
  412. const ODValueType kTEXTFileType = "+//ISO 9070/ANSI::113722::US::CI LABS::MacOS:FileType:TEXT";
  413. const ODValueType kHFSFlavorType = "+//ISO 9070/ANSI::113722::US::CI LABS::MacOS:ScrapType:hfs ";
  414.  
  415. ODDropResult MyPartDrop(MyPart* somSelf,
  416.  Environment* ev,
  417.  ODDragItemIterator* dropInfo,
  418.  ODFacet* facet,
  419.  ODPoint* where)
  420. {
  421.  ......
  422.  for (dropSU = dropInfo->First(ev);  dropInfo->IsNotComplete(ev); dropSU = dropInfo->Next(ev))
  423.  {
  424.    // Get the HFS flavor.
  425.    dropSU->Focus(ev, kODPropContents, kODPosUndefined, kHFSFlavorType, 0, kODPosUndefined);  
  426.    ODByteArray ba;
  427.    dropSU->GetValue(ev, sizeof(HFSFlavor), &ba);
  428.  
  429.    // The incoming file may be an alias file. If the actual file is desired, the destination should
  430.    // resolve the alias to find the file spec of the actual file.
  431.    ......
  432.  
  433.    // Use the File Spec in HFS flavor to open the file.
  434.    short fileRefNum;
  435.    OSErr err = FSpOpenDF(&(((HFSFlavor*) ba._buffer)->fileSpec), fsRdPerm, &fileRefNum);
  436.  
  437.    // You can do whatever you want to the file e.g., incorporating the data into your internal data structure.
  438.    ......
  439.  
  440.    // Cleanup
  441.    ODDisposePtr(ba._buffer);
  442.    FSClose(fileRefNum);
  443.   }
  444.   ......
  445.  
  446.  
  447. Embedding data from a non-OpenDoc document
  448.  
  449. Even when the destination part cannot incorporate the data, there may be other parts on the system which can. Following the OpenDoc model, the destination part can then embed the non-OpenDoc file and let another Part Editor handle the data.
  450.  
  451. From the destination's point of view, the recipe for embedding data from a non-OpenDoc documentan OpenDoc part is no different from embedding  an OpenDoc part. It first clones the Content Storage Unit and then it calls GetPart using the cloned Storage Unit. The OpenDoc Binding mechanism will use the corresponding Part Editor to manipulate the content of the cloned Storage Unit.
  452.  
  453. Here's what the destination part should do in its Drop method:
  454.  
  455. ......
  456.  
  457. ODDraft* dadDraft = dropSU->GetDraft(ev);
  458. ODDraft* myDraft = somSelf->GetStorageUnit(ev)->GetDraft(ev);
  459. ODDraftKey key = 0;
  460. ODID newPartID = kODNULLID;
  461. ODPart* newPart = kODNULL;
  462.  
  463. ODVolatile(key);
  464. ODVolatile(newPartID);
  465.  
  466. SOM_TRY
  467.  
  468.  // Begin a transfer from the drag-and-drop container into this draft.
  469.  key = dadDraft->BeginClone(ev, myDraft, facet->GetFrame(ev), kODClonePaste);
  470.  
  471.  // Clone the Storage Unit (which contains information on the
  472.  // non-OpenDoc document) from the drag-and-drop container.
  473.  //   Since we are cloning only a Storage Unit, there is no scoping. 0
  474.  //   is passed in to indicate that.
  475.  newPartID = dadDraft->Clone(ev, key, dropSU->GetID(ev), 0, 0);
  476.  
  477.  dadDraft->EndClone(ev, key);
  478.  
  479. SOM_CATCH_ALL
  480.  
  481.  if ( key != 0 )
  482.   dadDraft->AbortClone(ev, key);
  483.  
  484.  newPartID = kODNULLID;
  485.  
  486. SOM_ENDTRY
  487.  
  488. // Get the new embedded part
  489.  if (newPartID == kODNULLID) {
  490.   // cleanup and return error
  491.  }
  492.  else {
  493.   newPart = myDraft->AcquirePart(ev, newPartID);
  494.  }
  495.  
  496. ......
  497.  
  498. Here's what the newly created part should do in its InitPartFromStorage method:
  499.  
  500.  ......
  501.  
  502.  // myStorageUnit is a parameter to InitPartFromStorage.
  503.  if (myStorageUnit->Exists(ev, kODPropContents, kTEXTFileType, 0))
  504.  {
  505.   // Get the HFS flavor.
  506.   myStorageUnit->Focus(ev, kODPropContents, kODPosUndefined, kHFSFlavorType, 0, kODPosUndefined);  
  507.   HFSFlavor hfsFlavor;
  508.   ODByteArray ba;
  509.   myStorageUnit->GetValue(ev, sizeof(HFSFlavor), &ba);
  510.  
  511.   // Use the File Spec in HFS flavor to open the file.
  512.   short fileRefNum;
  513.   OSErr err = FSpOpenDF(&(((HFSFlavor*) ba._buffer)->fileSpec), fsRdPerm, &fileRefNum);
  514.  
  515.   // Read in the data (and possibly resources) from the file.
  516.   ......
  517.  
  518.   // Cleanup
  519.   ODDisposePtr(ba._buffer);
  520.   FSClose(fileRefNum);
  521.  
  522.   // A part should have a way to check that the storage unit contains 
  523.   // the appropriate properties and kinds. In our case, we will simply
  524.   // call our routine CheckPropertiesAndValues. This routine will
  525.   // probably remove the undesired values and add the primary value 
  526.   // kind to myStorageUnit.
  527.   CheckPropertiesAndValues(ev, myStorageUnit);
  528.  }
  529.  
  530.  ......
  531.  
  532.  
  533.  
  534.